home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / sml_nj / 93src.lha / src / util / pp.sml < prev    next >
Encoding:
Text File  |  1993-01-27  |  19.5 KB  |  557 lines

  1. (* pp.sml *)
  2.  
  3. (* This defines an abstract type (ppstream) and associated operations. A
  4.    ppstream is an outstream that contains prettyprinting commands. The
  5.    commands are placed in the stream by various function calls listed below.
  6.    Periodically, an (mostly) invisible process goes through the stream 
  7.    executing commands in the order in which they were added to the stream.
  8.  
  9.    One obtains a ppstream by "mk_ppstream". Its arguments are "linewidth"
  10.    (the maximum width in characters of the prettyprinted output) and 
  11.    "consumer" (the function that writes strings to the display). 
  12.    One can get these parameters from a ppstream by "dest_ppstream".
  13.  
  14.    Then there are the usual Oppen primitives for adding commands into the
  15.    stream: begin_block, end_block, add_string, add_break, and add_newline.
  16.  
  17.    There are two operations on the stream. "clear_ppstream" is used
  18.    to restart a given stream, while keeping everything else about the stream 
  19.    intact. An example of its use is when an error occurs during 
  20.    prettyprinting; in that case the top level printing function can catch
  21.    the exception and clear the ppstream. "flush_ppstream" is used at the 
  22.    end of inserting commands, to order the invisible process to execute all
  23.    remaining  commands in the stream. The last thing that flush_ppstream does 
  24.    is call clear_ppstream.
  25.  
  26.    There is also an operation that hides the state-based implementation of
  27.    ppstream: "with_pp" takes a function operating on a ppstream, makes an
  28.    appropriate ppstream and applies the function to it, then flushes the 
  29.    pp_stream and returns the value of the function. The ppstream is thus
  30.    only a local entity and is left to be garbage collected.
  31. *)
  32.  
  33. signature PRETTYPRINT =
  34. sig
  35.   type ppstream
  36.   type ppconsumer (* = {consumer : string -> unit,
  37.                 linewidth : int,
  38.             flush : unit -> unit} *)
  39.   datatype break_style
  40.     = CONSISTENT
  41.     | INCONSISTENT
  42.  
  43.   exception PP_FAIL of string
  44.  
  45.   val mk_ppstream    : ppconsumer -> ppstream
  46.   val dest_ppstream  : ppstream -> ppconsumer
  47.   val add_break      : ppstream -> int * int -> unit
  48.   val add_newline    : ppstream -> unit
  49.   val add_string     : ppstream -> string -> unit
  50.   val begin_block    : ppstream -> break_style -> int -> unit
  51.   val end_block      : ppstream -> unit
  52.   val clear_ppstream : ppstream -> unit
  53.   val flush_ppstream : ppstream -> unit
  54.   val with_pp : ppconsumer -> (ppstream -> unit) -> unit
  55. end
  56.  
  57.  
  58. structure PrettyPrint : PRETTYPRINT =
  59. struct
  60. (* the functions and data for actually doing printing. *)
  61.  
  62. open Array infix 9 sub
  63. open PPQueue
  64.  
  65. exception PP_FAIL of string
  66.  
  67. datatype break_style = CONSISTENT | INCONSISTENT
  68.  
  69. datatype break_info
  70.   = FITS
  71.   | PACK_ONTO_LINE of int 
  72.   | ONE_PER_LINE of int
  73.  
  74. (* Some global values *)
  75. val INFINITY = 999999
  76.  
  77. abstype indent_stack = Istack of break_info list ref
  78. with
  79.   fun mk_indent_stack() = Istack (ref([]:break_info list))
  80.   fun clear_indent_stack (Istack stk) = (stk := ([]:break_info list))
  81.   fun top (Istack stk) =
  82.       case !stk
  83.         of nil => raise PP_FAIL "top: badly formed block"
  84.      | x::_ => x
  85.   fun push (x,(Istack stk)) = stk := x::(!stk)
  86.   fun pop (Istack stk) =
  87.       case !stk
  88.     of nil => raise PP_FAIL "pop: badly formed block"
  89.      | _::rest => stk := rest
  90. end
  91.  
  92. (* The delim_stack is used to compute the size of blocks. It is 
  93.    a stack of indices into the token buffer. The indices only point to
  94.    BBs, Es, and BRs. We push BBs and Es onto the stack until a BR
  95.    is encountered. Then we compute sizes and pop. When we encounter
  96.    a BR in the middle of a block, we compute the Distance_to_next_break
  97.    of the previous BR in the block, if there was one.
  98.  
  99.    We need to be able to delete from the bottom of the delim_stack, so 
  100.    we use a queue, treated with a stack discipline, i.e., we only add
  101.    items at the head of the queue, but can delete from the front or
  102.    back of the queue.
  103. *)
  104. abstype delim_stack = Dstack of int queue
  105. with
  106.   fun new_delim_stack i = Dstack(mk_queue i ~1)
  107.   fun reset_delim_stack (Dstack q) = clear_queue q
  108.  
  109.   fun pop_delim_stack (Dstack d) = de_queue Qfront d
  110.   fun pop_bottom_delim_stack (Dstack d) = de_queue Qback d
  111.  
  112.   fun push_delim_stack(i,Dstack d) = en_queue Qfront i d
  113.   fun top_delim_stack (Dstack d) = queue_at Qfront d
  114.   fun bottom_delim_stack (Dstack d) = queue_at Qback d
  115.   fun delim_stack_is_empty (Dstack d) = is_empty d
  116. end
  117.  
  118.  
  119. type block_info = { Block_size : int ref, 
  120.                     Block_offset : int, 
  121.                     How_to_indent : break_style }
  122.  
  123.  
  124. (* Distance_to_next_break includes Number_of_blanks. Break_offset is
  125.    a local offset for the break. BB represents a sequence of contiguous
  126.    Begins. E represents a sequence of contiguous Ends.
  127. *)
  128. datatype pp_token 
  129.   = S of  {String : string, Length : int}
  130.   | BB of {Pblocks : block_info list ref,   (* Processed   *)
  131.            Ublocks : block_info list ref}  (* Unprocessed *)
  132.   | E of  {Pend : int ref, Uend : int ref}
  133.   | BR of {Distance_to_next_break : int ref,
  134.            Number_of_blanks : int,
  135.            Break_offset : int}
  136.  
  137.  
  138. (* The initial values in the token buffer *)
  139. val initial_token_value = S{String = "", Length = 0}
  140.  
  141. datatype ppstream = 
  142.   PPS of
  143.      {consumer : string -> unit,
  144.       linewidth : int,
  145.       flush : unit -> unit,
  146.       the_token_buffer : pp_token array,
  147.       the_delim_stack : delim_stack,
  148.       the_indent_stack : indent_stack,
  149.       ++ : int ref -> unit,    (* increment circular buffer index *)
  150.       space_left : int ref,    (* remaining columns on page *)
  151.       left_index : int ref,    (* insertion index *)
  152.       right_index : int ref,   (* output index *)
  153.       left_sum : int ref,      (* size of strings and spaces inserted *)
  154.       right_sum : int ref}     (* size of strings and spaces printed *)
  155.  
  156.    
  157. type ppconsumer = {consumer : string -> unit,
  158.            linewidth : int, 
  159.            flush : unit -> unit}
  160.  
  161. fun mk_ppstream {consumer,linewidth,flush} =
  162.     if (linewidth<5)
  163.     then raise PP_FAIL "linewidth too_small"
  164.     else let val buf_size = 3*linewidth
  165.       in PPS{consumer = consumer,
  166.          linewidth = linewidth,
  167.          flush = flush,
  168.          the_token_buffer = array(buf_size, initial_token_value),
  169.          the_delim_stack = new_delim_stack buf_size,
  170.          the_indent_stack = mk_indent_stack (),
  171.          ++ = fn i => i := ((!i + 1) mod buf_size),
  172.          space_left = ref linewidth,
  173.          left_index = ref 0, right_index = ref 0,
  174.          left_sum = ref 0, right_sum = ref 0}
  175.      end
  176.  
  177. fun dest_ppstream(PPS{consumer,linewidth,flush, ...}) =
  178.     {consumer=consumer,linewidth=linewidth,flush=flush}
  179.  
  180. local
  181.   val space = " "
  182.   fun mk_space (0,s) = implode s
  183.     | mk_space (n,s) = mk_space((n-1), (space::s))
  184.   val space_table = Vector.tabulate(100, fn i => mk_space(i,[]))
  185.   fun nspaces n =
  186.       Vector.sub(space_table,n)
  187.       handle Vector.Subscript =>
  188.     if n < 0
  189.     then ""
  190.     else let val n2 = n div 2
  191.          val n2_spaces = nspaces n2
  192.          val extra = if (n = (2*n2)) then "" else space
  193.           in implode [n2_spaces, n2_spaces, extra]
  194.          end
  195. in
  196.   fun cr_indent (ofn, i) = ofn ("\n"^(nspaces i))
  197.   fun indent (ofn,i) = ofn (nspaces i)
  198. end
  199.  
  200.  
  201. (* Print a the first member of a contiguous sequence of Begins. If there
  202.    are "processed" Begins, then take the first off the list. If there are
  203.    no processed Begins, take the last member off the "unprocessed" list.
  204.    This works because the unprocessed list is treated as a stack, the
  205.    processed list as a FIFO queue. How can an item on the unprocessed list
  206.    be printable? Because of what goes on in add_string. See there for details.
  207. *)
  208. fun print_BB (_,{Pblocks = ref [], Ublocks = ref []}) = raise PP_FAIL "print_BB"
  209.   | print_BB (PPS{the_indent_stack,linewidth,space_left=ref sp_left,...},
  210.              {Pblocks as ref({How_to_indent=CONSISTENT,Block_size,
  211.                               Block_offset}::rst),
  212.               Ublocks=ref[]}) =
  213.        (push ((if (!Block_size > sp_left)  
  214.                then ONE_PER_LINE (linewidth - (sp_left - Block_offset))
  215.                else FITS),
  216.           the_indent_stack);
  217.         Pblocks := rst)
  218.   | print_BB(PPS{the_indent_stack,linewidth,space_left=ref sp_left,...},
  219.              {Pblocks as ref({Block_size,Block_offset,...}::rst),Ublocks=ref[]}) =
  220.        (push ((if (!Block_size > sp_left)
  221.                then PACK_ONTO_LINE (linewidth - (sp_left - Block_offset))
  222.                else FITS),
  223.           the_indent_stack);
  224.         Pblocks := rst)
  225.   | print_BB (PPS{the_indent_stack, linewidth, space_left=ref sp_left,...},
  226.               {Ublocks,...}) = 
  227.       let fun pr_end_Ublock [{How_to_indent=CONSISTENT,Block_size,Block_offset}] l =
  228.         (push ((if (!Block_size > sp_left)  
  229.             then ONE_PER_LINE (linewidth - (sp_left - Block_offset))
  230.             else FITS),
  231.                the_indent_stack);
  232.          rev l)
  233.         | pr_end_Ublock [{Block_size,Block_offset,...}] l =
  234.         (push ((if (!Block_size > sp_left)  
  235.             then PACK_ONTO_LINE (linewidth - (sp_left - Block_offset))
  236.             else FITS),
  237.                the_indent_stack);
  238.          rev l)
  239.         | pr_end_Ublock (a::rst) l = pr_end_Ublock rst (a::l)
  240.        in Ublocks := pr_end_Ublock(!Ublocks) []
  241.       end
  242.  
  243.  
  244. (* Uend should always be 0 when print_E is called. *)
  245. fun print_E (_,{Pend = ref 0, Uend = ref 0}) = raise PP_FAIL "print_E"
  246.   | print_E (istack,{Pend, ...}) =
  247.       let fun pop_n_times 0 = ()
  248.         | pop_n_times n = (pop istack; pop_n_times(n-1))
  249.        in pop_n_times(!Pend); Pend := 0
  250.       end
  251.  
  252.  
  253. (* "cursor" is how many spaces across the page we are. *)
  254.  
  255. fun print_token(PPS{consumer,space_left,...}, S{String,Length}) = 
  256.       (consumer String; 
  257.        space_left := (!space_left) - Length)
  258.   | print_token(ppstrm,BB b) = print_BB(ppstrm,b)
  259.   | print_token(PPS{the_indent_stack,...},E e) = 
  260.       print_E (the_indent_stack,e)
  261.   | print_token (PPS{the_indent_stack,space_left,consumer,linewidth,...},
  262.                  BR{Distance_to_next_break,Number_of_blanks,Break_offset}) =
  263.      (case (top the_indent_stack)
  264.         of FITS =>
  265.          (space_left := (!space_left) - Number_of_blanks;
  266.               indent (consumer,Number_of_blanks))
  267.          | (ONE_PER_LINE cursor) => 
  268.              let val new_cursor = cursor + Break_offset
  269.               in space_left := linewidth - new_cursor; 
  270.                  cr_indent (consumer,new_cursor)
  271.          end
  272.          | (PACK_ONTO_LINE cursor) => 
  273.          if (!Distance_to_next_break > (!space_left))
  274.          then let val new_cursor = cursor + Break_offset
  275.            in space_left := linewidth - new_cursor;
  276.               cr_indent(consumer,new_cursor)
  277.           end
  278.          else (space_left := !space_left - Number_of_blanks;
  279.            indent (consumer,Number_of_blanks)))
  280.  
  281.  
  282. fun clear_ppstream(PPS{the_token_buffer, the_delim_stack, 
  283.                        the_indent_stack,left_sum, right_sum, 
  284.                        left_index, right_index,space_left,linewidth,...}) =
  285.     let val buf_size = 3*linewidth
  286.     fun set i =
  287.         if (i = buf_size)
  288.         then ()
  289.         else (update(the_token_buffer,i,initial_token_value); 
  290.           set (i+1))
  291.      in set 0;
  292.     clear_indent_stack the_indent_stack;
  293.     reset_delim_stack the_delim_stack; 
  294.     left_sum := 0; right_sum := 0; 
  295.     left_index := 0; right_index := 0;
  296.     space_left := linewidth
  297.     end
  298.  
  299.  
  300. (* Move insertion head to right unless adding a BB and already at a BB,
  301.    or unless adding an E and already at an E.
  302. *)
  303. fun BB_inc_right_index(PPS{the_token_buffer, right_index, ++,...})=
  304.     case (the_token_buffer sub (!right_index))
  305.       of (BB _) => ()
  306.        | _ => ++right_index
  307.  
  308. fun E_inc_right_index(PPS{the_token_buffer,right_index, ++,...})=
  309.     case (the_token_buffer sub (!right_index))
  310.       of (E _) => ()
  311.        | _ => ++right_index
  312.  
  313.  
  314. fun pointers_coincide(PPS{left_index,right_index,the_token_buffer,...}) =
  315.     (!left_index = !right_index) andalso
  316.     (case (the_token_buffer sub (!left_index))
  317.        of (BB {Pblocks = ref [], Ublocks = ref []}) => true
  318.     | (BB _) => false
  319.     | (E {Pend = ref 0, Uend = ref 0}) => true
  320.     | (E _) => false
  321.     | _ => true)
  322.  
  323. fun advance_left (ppstrm as PPS{consumer,left_index,left_sum,
  324.                                 the_token_buffer,++,...},
  325.                   instr) =
  326.     let val NEG = ~1
  327.     val POS = 0
  328.     fun inc_left_sum (BR{Number_of_blanks, ...}) = 
  329.          left_sum := (!left_sum) + Number_of_blanks
  330.       | inc_left_sum (S{Length, ...}) = left_sum := (!left_sum) + Length
  331.       | inc_left_sum _ = ()
  332.  
  333.     fun last_size [{Block_size, ...}:block_info] = !Block_size
  334.       | last_size (_::rst) = last_size rst
  335.     fun token_size (S{Length, ...}) = Length
  336.       | token_size (BB b) =
  337.          (case b
  338.         of {Pblocks = ref [], Ublocks = ref []} => raise PP_FAIL "BB_size"
  339.              | {Pblocks as ref(_::_),Ublocks=ref[]} => POS
  340.          | {Ublocks, ...} => last_size (!Ublocks))
  341.       | token_size (E{Pend = ref 0, Uend = ref 0}) =
  342.           raise PP_FAIL "token_size.E"
  343.       | token_size (E{Pend = ref 0, ...}) = NEG
  344.       | token_size (E _) = POS
  345.       | token_size (BR {Distance_to_next_break, ...}) = !Distance_to_next_break
  346.     fun loop (instr) =
  347.         if (token_size instr < 0)  (* synchronization point; cannot advance *)
  348.         then ()
  349.         else (print_token(ppstrm,instr);
  350.           inc_left_sum instr;
  351.           if (pointers_coincide ppstrm)
  352.           then ()
  353.           else (* increment left index *)
  354.  
  355.     (* When this is evaluated, we know that the left_index has not yet
  356.        caught up to the right_index. If we are at a BB or an E, we can 
  357.        increment left_index if there is no work to be done, i.e., all Begins 
  358.        or Ends have been dealt with. Also, we should do some housekeeping and 
  359.        clear the buffer at left_index, otherwise we can get errors when 
  360.        left_index catches up to right_index and we reset the indices to 0. 
  361.        (We might find ourselves adding a BB to an "old" BB, with the result 
  362.        that the index is not pushed onto the delim_stack. This can lead to 
  363.        mangled output.)
  364.     *)
  365.                (case (the_token_buffer sub (!left_index))
  366.               of (BB {Pblocks = ref [], Ublocks = ref []}) => 
  367.                    (update(the_token_buffer,!left_index,
  368.                        initial_token_value); 
  369.                 ++left_index)
  370.                | (BB _) => ()
  371.                | (E {Pend = ref 0, Uend = ref 0}) => 
  372.                    (update(the_token_buffer,!left_index,
  373.                        initial_token_value); 
  374.                 ++left_index)
  375.                | (E _) => ()
  376.                | _ => ++left_index;
  377.             loop (the_token_buffer sub (!left_index))))
  378.      in loop instr
  379.     end
  380.  
  381.  
  382. fun begin_block (ppstrm as PPS{the_token_buffer, the_delim_stack,left_index,
  383.                                left_sum, right_index, right_sum,...})
  384.                 style offset = 
  385.    (if (delim_stack_is_empty the_delim_stack)
  386.     then (left_index := 0;
  387.       left_sum := 1;
  388.       right_index := 0;
  389.       right_sum := 1)
  390.     else BB_inc_right_index ppstrm;
  391.     case (the_token_buffer sub (!right_index))
  392.       of (BB {Ublocks, ...}) => 
  393.        Ublocks := {Block_size = ref (~(!right_sum)),
  394.                Block_offset = offset,
  395.                How_to_indent = style}::(!Ublocks)
  396.        | _ => (update(the_token_buffer, !right_index,
  397.               BB{Pblocks = ref [],
  398.              Ublocks = ref [{Block_size = ref (~(!right_sum)),
  399.                      Block_offset = offset,
  400.                      How_to_indent = style}]});
  401.            push_delim_stack (!right_index, the_delim_stack)))
  402.  
  403.  
  404. fun end_block(ppstrm as PPS{the_token_buffer,the_delim_stack,right_index,...})=
  405.     if (delim_stack_is_empty the_delim_stack)
  406.     then print_token(ppstrm,(E{Pend = ref 1, Uend = ref 0}))
  407.     else (E_inc_right_index ppstrm;
  408.       case (the_token_buffer sub (!right_index))
  409.         of (E{Uend, ...}) => inc Uend
  410.          | _ => (update(the_token_buffer,!right_index,
  411.                 E{Uend = ref 1, Pend = ref 0});
  412.              push_delim_stack (!right_index, the_delim_stack)))
  413.  
  414. local
  415.   fun check_delim_stack(PPS{the_token_buffer,the_delim_stack,right_sum,...}) =
  416.       let fun check k =
  417.           if (delim_stack_is_empty the_delim_stack)
  418.           then ()
  419.           else case(the_token_buffer sub (top_delim_stack the_delim_stack))
  420.              of (BB{Ublocks as ref ((b as {Block_size, ...})::rst),
  421.                 Pblocks}) => 
  422.                if (k>0)
  423.                then (Block_size := !right_sum + !Block_size;
  424.                  Pblocks := b :: (!Pblocks);
  425.                  Ublocks := rst;
  426.                  if (List.length rst = 0)
  427.                  then pop_delim_stack the_delim_stack
  428.                  else ();
  429.                  check(k-1))
  430.                else ()
  431.               | (E{Pend,Uend}) =>
  432.                (Pend := (!Pend) + (!Uend);
  433.                 Uend := 0;
  434.                 pop_delim_stack the_delim_stack;
  435.                 check(k + !Pend))
  436.               | (BR{Distance_to_next_break, ...}) => 
  437.                (Distance_to_next_break :=
  438.                   !right_sum + !Distance_to_next_break;
  439.                 pop_delim_stack the_delim_stack;
  440.                 if (k>0) 
  441.                 then check k
  442.                 else ())
  443.               | _ => raise PP_FAIL "check_delim_stack.catchall"
  444.        in check 0
  445.       end
  446. in
  447.  
  448.   fun add_break (ppstrm as PPS{the_token_buffer,the_delim_stack,left_index,
  449.                    right_index,left_sum,right_sum, ++, ...})
  450.         (n,break_offset) =
  451.       (if (delim_stack_is_empty the_delim_stack)
  452.        then (left_index := 0; right_index := 0;
  453.          left_sum := 1;   right_sum := 1)
  454.        else ++right_index;
  455.        update(the_token_buffer, !right_index,
  456.           BR{Distance_to_next_break = ref (~(!right_sum)),
  457.          Number_of_blanks = n,
  458.          Break_offset = break_offset});
  459.        check_delim_stack ppstrm;
  460.        right_sum := (!right_sum) + n;
  461.        push_delim_stack (!right_index,the_delim_stack)) 
  462.  
  463.   fun flush_ppstream0(ppstrm as PPS{the_delim_stack,the_token_buffer, flush,
  464.                     left_index,...}) =
  465.       (if (delim_stack_is_empty the_delim_stack)
  466.        then ()
  467.        else (check_delim_stack ppstrm;
  468.          advance_left(ppstrm, the_token_buffer sub (!left_index)));
  469.        flush())
  470.  
  471. end (* local *)
  472.  
  473.  
  474. fun flush_ppstream ppstrm =
  475.     (flush_ppstream0 ppstrm;
  476.      clear_ppstream ppstrm)
  477.  
  478. fun add_string (ppstrm as PPS{the_token_buffer,the_delim_stack,consumer,
  479.                               right_index,right_sum,left_sum,
  480.                   left_index,space_left,++,...})
  481.                s =
  482.     let fun fnl [{Block_size, ...}:block_info] = Block_size := INFINITY
  483.       | fnl (_::rst) = fnl rst
  484.  
  485.     fun set(dstack,BB{Ublocks as ref[{Block_size,...}:block_info],...}) = 
  486.           (pop_bottom_delim_stack dstack;
  487.            Block_size := INFINITY)
  488.       | set (_,BB {Ublocks = ref(_::rst), ...}) = fnl rst
  489.       | set (dstack, E{Pend,Uend}) =
  490.           (Pend := (!Pend) + (!Uend);
  491.            Uend := 0;
  492.            pop_bottom_delim_stack dstack)
  493.       | set (dstack,BR{Distance_to_next_break,...}) = 
  494.           (pop_bottom_delim_stack dstack;
  495.            Distance_to_next_break := INFINITY)
  496.       | set _ = raise (PP_FAIL "add_string.set")
  497.  
  498.     fun check_stream () =
  499.         if ((!right_sum - !left_sum) > !space_left)
  500.         then if (delim_stack_is_empty the_delim_stack)
  501.          then ()
  502.          else let val i = bottom_delim_stack the_delim_stack
  503.                in if (!left_index = i)
  504.               then set (the_delim_stack, the_token_buffer sub i)
  505.               else ();
  506.               advance_left(ppstrm,
  507.                                        the_token_buffer sub (!left_index));
  508.                   if (pointers_coincide ppstrm)
  509.                   then ()
  510.                   else check_stream ()
  511.               end
  512.         else ()
  513.  
  514.     val slen = String.size s
  515.     val S_token = S{String = s, Length = slen}
  516.  
  517.     in if (delim_stack_is_empty the_delim_stack)
  518.        then print_token(ppstrm,S_token)
  519.        else (++right_index;
  520.              update(the_token_buffer, !right_index, S_token);
  521.              right_sum := (!right_sum)+slen;
  522.              check_stream ())
  523.    end
  524.  
  525.  
  526. (* Derived form. The +2 is for peace of mind *)
  527. fun add_newline (ppstrm as PPS{linewidth, ...}) = 
  528.     add_break ppstrm (linewidth+2,0)
  529.  
  530. (* Derived form. Builds a ppstream, sends pretty printing commands called in
  531.    f to the ppstream, then flushes ppstream.
  532. *)
  533. fun with_pp ppconsumer ppfn = 
  534.    let val ppstrm = mk_ppstream ppconsumer
  535.     in ppfn ppstrm;
  536.        flush_ppstream0 ppstrm
  537.    end
  538.    handle PP_FAIL msg =>
  539.      (System.Print.say ">>>> PP_FAIL: ";
  540.       System.Print.say msg;
  541.       System.Print.say "\n")
  542.  
  543. (*
  544. (* Derived form. Makes an outstream be the target of prettyprinting. *)
  545. fun out_ppstream outstrm n =
  546.     mk_ppstream{consumer = outputc outstrm,
  547.         linewidth = n, 
  548.         flush = flush_out outstrm}
  549.  
  550. (* Derived form. Makes a ppstream that observes system parameters. *)
  551. fun mk_std_ppstream() =
  552.     out_ppstream (!System.Print.out) (!System.Print.linewidth);
  553.  
  554. *)
  555.  
  556. end (* PrettyPrint *)
  557.